local extension = Package:new("zhonger_heg_jin")
extension.extensionName = "zhonger_heg"
extension.game_modes_whitelist = {"zhonger_heg_mode"}

local U = require "packages/utility/utility"
local S = require "packages/seaborn/SeaFunc"

Fk:loadTranslationTable{
  ["zhonger_heg_jin"] = "久分必合·晋",
  ["jin_heg"] = "晋",
  ["jinh"] = "晋",

  ["@$Publishing"] = "明置",
  ["Published"] = "明置中",
}

--- 系列特色 --- 明置相关
local Pub = "@$Publishing" --- 明置Mark

--- 明置
---@param player ServerPlayer @ 执行者
---@param cards int[] @ 牌的id表
local function publish(player, cards)
  local room = player.room
  room:setPlayerMark(player, Pub, cards)
end

--- 判断牌是否明置
---@param player ServerPlayer @ 执行者
---@param id int @ 牌的id 
---@return bool
local function isPublished(player, id)
  local published_cards = U.getMark(player, Pub)
  return table.contains(published_cards, id)
end

--- 取消明置
---@param player ServerPlayer @ 执行者
---@param card int @ 牌的id
local function unpublish(player, card)
  local room = player.room
  if isPublished(player, card) then
    local cards = U.getMark(player, Pub)
    table.removeOne(cards, card)
    room:setPlayerMark(player, Pub, #cards > 0 and cards or 0)
  end
end

--- 离开手牌取消明置
local publish_refresher = fk.CreateTriggerSkill{
  name = "publish_refresher",
  global = true,

  refresh_events = {fk.BeforeCardsMove},
  can_refresh = function(self, event, target, player, data)
    return true
  end,
  on_refresh = function(self, event, target, player, data)
    local published_cards = U.getMark(player, Pub)
    if table.find(player:getMarkNames(), function(name)
      return string.find(name, "KeepShowing")
    end) then
      published_cards = player:getCardIds("h")
    end
    if #published_cards ~= 0 then
      for _, move in ipairs(data) do
        for _, info in ipairs(move.moveInfo) do
          if info.fromArea == Card.PlayerHand and isPublished(player, info.cardId) then
            table.removeOne(published_cards, info.cardId)
          end
        end
      end
      if #published_cards == 0 then
        player.room:setPlayerMark(player, Pub, 0)
      else
        player.room:setPlayerMark(player, Pub, published_cards)
      end
    end
  end
}
S.skillInfo(publish_refresher, "明置[失去]", "明置的牌已失去")
Fk:addSkill(publish_refresher)

--- 01 --- 司马懿

local mark_name = "@$ze_yinsghi" -- 鹰视

--- 上标记
local function setUnvisableMark(player, mark)
  player:setMark(mark_name, mark)
  player:doNotify("SetPlayerMark", json.encode{ player.id, mark_name, mark})
end

local yingshi = fk.CreateTriggerSkill{
  name = "ze__yingshi",
  anim_type = "special",
  frequency = Skill.Compulsory,

  refresh_events = {fk.AfterCardsMove, fk.EventPhaseStart, fk.GeneralRevealed, fk.AfterPropertyChange, fk.EventPhaseEnd},
  can_refresh = function(self, event, target, player, data)
    return player:hasSkill(self) and player.phase == Player.Play and not player:isFakeSkill(self)
  end,
  on_refresh = function(self, event, target, player, data)
    local room = player.room
    local mark = U.getMark(player, mark_name)
    local toView = table.slice(room.draw_pile, 1, player.maxHp +1)
    if event == fk.EventPhaseEnd then
      setUnvisableMark(player, 0)
    elseif not mark.value or toView ~= mark.value then
      if table.contains({fk.EventPhaseStart}, event) then
        player:broadcastSkillInvoke(self.name)
      end
      setUnvisableMark(player, toView)
    end
  end,
}
S.skillInfo(yingshi, "鹰视",
  "锁定技，你的出牌阶段内，牌堆顶的X张牌对你可见（X为你的体力上限）。",
  {"鹰扬千里，明察秋毫。","鸢飞戾天，目入百川。"}
)

Fk:loadTranslationTable{
  [mark_name] = "鹰视",
}

local xiongzhi = fk.CreateActiveSkill{
  name = "ze__xiongzhi",
  anim_type = "offensive",
  frequency = Skill.Limited,
  can_use = function(self, player)
    return player:usedSkillTimes(self.name, Player.HistoryGame) == 0
  end,
  card_num = 0,
  card_filter = Util.FalseFunc,
  target_num = 0,
  target_filter = Util.FalseFunc,
  on_use = function(self, room, effect)
    local player = room:getPlayerById(effect.from)
    while true do
      if player.dead then return end
      local id = room:getNCards(1)
      room:moveCards({
        ids = id,
        toArea = Card.Processing,
        moveReason = fk.ReasonJustMove,
        skillName = self.name,
      })
      local card = Fk:getCardById(id[1], true)
      local tos = U.getDefaultTargets(player, card, true)
      if tos then
        local propompt = "#xiongzhi-use:::"..Fk:translate(card.name)
        local extra_data = { card = id[1], bypass_times = true }
        local _, ret = room:askForUseActiveSkill(player, "assign_cards_use", propompt, false, extra_data)
        if ret and ret.targets then
          tos = ret.targets
        end
      end
      if tos then
        room:useCard({
          card = card,
          from = player.id,
          tos = table.map(tos, function(p) return {p} end) ,
          skillName = self.name,
          extraUse = true,
        })
      else
        room:delay(800)
        room:moveCards({
          ids = id,
          fromArea = Card.Processing,
          toArea = Card.DrawPile,
          moveReason = fk.ReasonJustMove,
        })
        return
      end
    end
  end,
}
S.skillInfo(xiongzhi, "雄志",
  "限定技，出牌阶段，你可以连续展示并使用牌堆顶的牌，直到牌堆顶的牌不能被使用。",
  {"烈士雄心，志存高远。","乱世之中，唯我司马！"}
)

Fk:loadTranslationTable{
  ["#xiongzhi-use"] = "雄志：选择【%arg】的目标，使用【%arg】"
}

local quanbian = fk.CreateTriggerSkill{
  name = "ze__quanbian",
  anim_type = "drawcard",
  events = {fk.CardUsing, fk.CardResponding},
  can_trigger = function(self, event, target, player, data)
    if target == player and player:hasSkill(self) and player.phase == Player.Play and data.card.suit ~= Card.NoSuit and
      U.IsUsingHandcard(player, data) then
      local card_suit = data.card.suit
      local room = player.room
      local logic = room.logic
      local current_event = logic:getCurrentEvent()
      local mark_name = "quanbian_" .. data.card:getSuitString() .. "-phase"
      local mark = player:getMark(mark_name)
      if mark == 0 then
        logic:getEventsOfScope(GameEvent.UseCard, 1, function(e)
          local use = e.data[1]
          if use.from == player.id and use.card.suit == card_suit then
            mark = e.id
            room:setPlayerMark(player, mark_name, mark)
            return true
          end
          return false
        end, Player.HistoryPhase)
        logic:getEventsOfScope(GameEvent.RespondCard, 1, function(e)
          local use = e.data[1]
          if use.from == player.id and use.card.suit == card_suit then
            mark = math.max(e.id, mark)
            room:setPlayerMark(player, mark_name, mark)
            return true
          end
          return false
        end, Player.HistoryPhase)
      end
      return mark == current_event.id
    end
  end,
  on_trigger = function(self, event, target, player, data)
    local mark = player:getMark("@quanbian-phase")
    if mark == 0 then mark = {} end
    table.insert(mark, data.card:getSuitString(true))
    player.room:setPlayerMark(player, "@quanbian-phase", mark)
    self:doCost(event, target, player, data)
  end,
  on_use = function(self, event, target, player, data)
    local room = player.room
    local cards = room:getNCards(player.maxHp)
    local handcards = player:getCardIds("h")
    local to_ex = U.askForExchange(player, "Top", "hand_card", cards, handcards, "#quanbianze-exchange", 1)
    if #to_ex == 0 then return end
    local index = 0
    local cardA = table.find(cards, function(id)
      index = index + 1
      return table.contains(to_ex, id)
    end)
    local cardB = table.find(to_ex, function(id)
      return id ~= cardA
    end)
    room:moveCards({
      ids = cards,
      toArea = Card.Processing,
      skillName = self.name,
      moveReason = fk.ReasonExchange,
      proposer = player.id,
      moveVisible = false
    }, {
      ids = {cardB},
      from = player.id,
      toArea = Card.Processing,
      skillName = self.name,
      moveReason = fk.ReasonExchange,
      proposer = player.id,
      moveVisible = false
    })
    local moveInfos = {}
    if room:getCardArea(cardA) == Card.Processing then
      if player.dead then
        table.insert(moveInfos, {
          ids = {cardA},
          fromArea = Card.Processing,
          toArea = Card.DiscardPile,
          moveReason = fk.ReasonExchange,
          proposer = player.id,
          skillName = self.name
        })
      else
        table.insert(moveInfos, {
          ids = {cardA},
          fromArea = Card.Processing,
          to = player.id,
          toArea = Card.PlayerHand,
          moveReason = fk.ReasonExchange,
          proposer = player.id,
          skillName = self.name,
          moveVisible = false
        })
      end
    end
    table.remove(cards, index)
    table.insert(cards, index, cardB)
    cards = table.reverse(cards)
    cards = table.filter(cards, function(id)
      return room:getCardArea(id) == Card.Processing
    end)
    if #cards > 0 then
      table.insert(moveInfos, {
        ids = cards,
        toArea = Card.DrawPile,
        moveReason = fk.ReasonExchange,
        proposer = player.id,
        skillName = self.name,
        moveVisible = false
      })
    end
    if #moveInfos > 0 then
      room:moveCards(table.unpack(moveInfos))
    end
  end,
}
S.skillInfo(quanbian, "权变",
  "当你于出牌阶段首次使用或打出一种花色的手牌时，你可从用一张手牌替换牌堆顶X张牌中的一张（X为你的体力上限）。",
  {"筹权谋变，步步为营。","随机应变，谋国窃权。"}
)

Fk:loadTranslationTable{
  ["#quanbianze-exchange"] = "权变：可以用一张手牌替换其中的一张牌"
}

local simayi = General(extension, "jin_heg__simayi", "jinh", 4)  -- 司马懿
S.generalInfo(simayi, "司马懿", "通达权变", "中二国战", "", "", "六道目", "虎入骷冢，司马难兴。")
simayi:addSkill(yingshi)
simayi:addSkill(xiongzhi)
simayi:addSkill(quanbian)

--- 02 --- 司马师

local yimie = fk.CreateTriggerSkill{
  name = "ze__yimie",
  anim_type = "offensive",
  frequency = Skill.Compulsory,
  refresh_events = {fk.EnterDying},
  can_refresh = function(self, event, target, player, data)
    return player:hasSkill(self) and player.phase ~= Player.NotActive and table.contains(player.player_skills, self)
  end,
  on_refresh = function(self, event, target, player, data)
    player.room:notifySkillInvoked(player, self.name)
    player:broadcastSkillInvoke(self.name)
  end,
}
S.skillInfo(yimie, "夷灭",
  "锁定技，你的回合内，处于濒死状态的角色不能使用【桃】。",
  {"汝大逆不道，当死无赦！","斩草除根，灭其退路！"}
)

local yimie_prohibit = fk.CreateProhibitSkill{
  name = "#yimie_prohibit",
  prohibit_use = function(self, player, card)
    if card.name == "peach" and player.dying then
      local cfilter = function(p)
        return p.phase ~= Player.NotActive and p:hasSkill(yimie) and table.contains(p.player_skills, yimie)
      end
      return table.find(Fk:currentRoom().alive_players, cfilter)
    end
  end,
}
S.skillInfo(yimie_prohibit, "夷灭", "处于濒死状态的角色不能使用【桃】。")
yimie:addRelatedSkill(yimie_prohibit)

local tairan = fk.CreateTriggerSkill{
  name = "ze__tairan",
  anim_type = "defensive",
  frequency = Skill.Compulsory,
  events = {fk.TurnEnd, fk.EventPhaseStart},
  can_trigger = function(self, event, target, player, data)
    if target == player and player:hasSkill(self) then
      if event == fk.TurnEnd then
        return player:getHandcardNum() < player.maxHp
      elseif player.phase == Player.Play then
        local cfilter = function(id)
          local card = Fk:getCardById(id)
          return card:getMark("@@ze_tairan_Pub-inhand") > 0 and not player:prohibitDiscard(card)
        end
        return table.find(player:getCardIds("h"), cfilter)
      end
    end
  end,
  on_use = function(self, event, target, player, data)
    local room = player.room
    if event == fk.TurnEnd then
      local num = player.maxHp - player:getHandcardNum()
      if num > 0 then
        player:drawCards(num, self.name)
        local all_cards = player:getCardIds("h")
        local cards = table.filter(all_cards, function(id)
          if not isPublished(player, id) then
            return true
          end
        end)
        num = math.min(#cards, num)
        local selected, _ = U.askforChooseCardsAndChoice(player, cards, {"OK"}, self.name, "#tairanze-choose:::"..num, nil, num, num, all_cards)
        publish(player, selected)
        for _, id in ipairs(selected) do
          local card = Fk:getCardById(id)
          room:setCardMark(card, "@@ze_tairan_Pub-inhand", 1)
        end
      end
    else
      local cfilter = function(id)
        local card = Fk:getCardById(id)
        return card:getMark("@@ze_tairan_Pub-inhand") > 0 and not player:prohibitDiscard(card)
      end
      if not player.dead then
        local cards = table.filter(player:getCardIds(Player.Hand), cfilter)
        if #cards == 0 then return end
        room:throwCard(cards, self.name, player, player)
      end
    end
  end,
}
S.skillInfo(tairan, "泰然",
  "锁定技，回合结束时，你摸X张牌（X为你的体力上限-手牌数），明置等量张手牌；出牌阶段开始时，你弃置以此法明置的牌。",
  {"撼山易，撼我司马氏难。","云卷云舒，处之泰然。"}
)

Fk:loadTranslationTable{
  ["#tairanze-choose"] = "泰然：请选 %arg 张手牌明置",
  ["@@ze_tairan_Pub-inhand"] = "泰然"
}

local simashi = General(extension, "jin_heg__simashi", "jinh", 4) -- 司马师
S.generalInfo(simashi, "司马师", "摧坚荡异", "中二国战", "", "", "拉布拉卡", "子上，这是为兄给你打下的江山……")
simashi:addSkill(yimie)
simashi:addSkill(tairan)

--- 司马昭 ---

local choufa = fk.CreateActiveSkill{
  name = "ze__choufa",
  mute = true,
  can_use = function(self, player, card, extra_data)
    return player:usedSkillTimes(self.name, Player.HistoryPhase) == 0
  end,
  card_num = 0,
  card_filter = Util.FalseFunc,
  target_num = 1,
  target_filter = function(self, to_select, selected, selected_cards, card, extra_data)
    if #selected == 0 then
      return to_select ~= Self.id and Fk:currentRoom():getPlayerById(to_select):getHandcardNum() > 0
    end
  end,
  on_use = function(self, room, effect)
    local player = room:getPlayerById(effect.from)
    local target = room:getPlayerById(effect.tos[1])
    if target == player then
      player:broadcastSkillInvoke(self.name, 1)
      room:notifySkillInvoked(player, self.name, "offensive")
    else
      player:broadcastSkillInvoke(self.name, 2)
      room:notifySkillInvoked(player, self.name, "control")
    end
    local id = room:askForCardsChosen(player, target, 1, 1, "h", self.name, "#choufa-show:"..effect.tos[1])[1]
    local card = Fk:getCardById(id)
    if not card then return end
    target:showCards(card)
    local type = card:getTypeString()
    if type == "notype" then return end
    local mark = U.getMark(target, "@choufa-turn")
    table.insertIfNeed(mark, type)
    room:setPlayerMark(target, "@choufa-turn", mark)
  end,
}
S.skillInfo(choufa, "筹伐",
  "出牌阶段限一次，你可展示一名其他角色的张手牌，然后令其本回合与此牌类别不同的手牌均视为【杀】。",
  {"敌军势大与否，无碍我自计定施。","汝竭力强攻，也只徒燥军心。"}
)

local choufa_filter = fk.CreateFilterSkill{
  name = "#choufa_filter",
  card_filter = function(self, to_select, player)
    local mark = U.getMark(player, "@choufa-turn")
    return player:hasSkill(self) and #mark > 0 and table.contains(mark, to_select:getTypeString())
  end,
  view_as = function(self, to_select)
    return Fk:cloneCard("slash", to_select.suit, to_select.number)
  end,
}
choufa:addRelatedSkill(choufa_filter)

Fk:loadTranslationTable{
  ["@choufa-turn"] = "筹伐",
  ["#choufa-show:"] = "筹伐：请展示 %src 的一张手牌"
}

local zhaoran = fk.CreateTriggerSkill{
  name = "ze__zhaoran",
  mute = true,
  events = {fk.EventPhaseStart, fk.AfterCardsMove},
  can_trigger = function(self, event, target, player, data)
    if player:hasSkill(self) and player.phase == Player.Play then
      if event == fk.EventPhaseStart then
        return target == player
      else
        return player:usedSkillTimes(self.name, Player.HistoryPhase) > 0
      end
    end
  end,
  on_trigger = function(self, event, target, player, data)
    if event == fk.EventPhaseStart then
      self:doCost(event, target, player, data)
    else
      local mark = U.getMark(player, "@zhaoran-phase")
      for _, move in ipairs(data) do
        if  move.from == player.id then
          for _, info in ipairs(move.moveInfo) do
            local card = Fk:getCardById(info.cardId)
            local hand_cards = table.map((player:getCardIds("h")), Util.Id2CardMapper)
            local suits = table.map(hand_cards, function(c) return c:getSuitString(true) end)
            if info.fromArea == Card.PlayerHand and not table.contains(suits, card:getSuitString(true))
              and not table.contains(mark, card:getSuitString(true)) then
              table.insert(mark, card:getSuitString(true))
              player.room:setPlayerMark(player, "@zhaoran-phase", mark)
              self:doCost(event, target, player, data)
            end
          end
        end
      end
    end
  end,
  on_cost = function(self, event, target, player, data)
    local room = player.room
    if event == fk.EventPhaseStart then
      return room:askForSkillInvoke(player, self.name, data)
    else
      local filter = function(p) return not p:isNude() end
      local targets = table.map(table.filter(room:getOtherPlayers(player), filter), Util.IdMapper)
      local to = room:askForChoosePlayers(player, targets, 1, 1, "#zhaoran-choose", self.name, true)
      if to then
        self.cost_data = to
      else
        self.cost_data = "drawOneCard"
      end
      return true
    end
  end,
  on_use = function(self, event, target, player, data)
    local room = player.room
    if event == fk.EventPhaseStart then
      player:broadcastSkillInvoke(self.name, math.random(1,2))
      room:notifySkillInvoked(player, self.name, "special")
      room:setPlayerMark(player, "zhaoran_KeepShowing-phase", 1)
      publish(player, player:getCardIds("h"))
      local unPub = function()
        for _, id in ipairs(player:getCardIds("h")) do
          local card = Fk:getCardById(id)
          if not table.find(card:getMarkNames(), function(name) return string.find(name, "Publishing") end) then
            unpublish(player, id)
          end
        end
      end
      room.logic:getCurrentEvent():findParent(GameEvent.Phase):addCleaner(unPub)
    else
      if #self.cost_data > 0 then
        player:broadcastSkillInvoke(self.name, math.random(3,4))
        room:notifySkillInvoked(player, self.name, "offensive")
        local to = room:getPlayerById(self.cost_data[1])
        local cid = room:askForCardsChosen(player, to, 1, 1, "he", self.name, "#zhaoran-discard:"..to.id)
        room:throwCard(cid, self.name, to, player)
      else
        player:broadcastSkillInvoke(self.name, math.random(1,2))
        room:notifySkillInvoked(player, self.name, "drawcard")
        player:drawCards(1, self.name)
      end
    end
  end,
}
S.skillInfo(zhaoran, "昭然",
  "出牌阶段开始时，你可以令你此阶段手牌区里的牌均为明置状态，然后你此阶段首次失去每种花色的最后一张手牌后，摸一张牌或弃置一名其他角色的一张牌。",
  {"行明动正，何惧他人讥毁。", "大业之举，岂因宵小而动？", "胆敢谤言惑众，这就是下场！", "今天，就拿你来杀鸡儆猴。"}
)

Fk:loadTranslationTable{
  ["@zhaoran-phase"] = "昭然",
  ["#zhaoran-choose"] = "昭然：选择一名角色，弃置其一张牌；点取消则摸一张牌",
  ["zhaoran_KeepShowing-phase"] = "昭然[展示]",
  ["#zhaoran-discard:"] = "昭然：请弃置 %src 的一张牌"
}

local simazhao = General(extension, "jin_heg__simazhao", "jinh", 3) -- 司马昭
S.generalInfo(simazhao, "司马昭", "嘲风开天", "中二国战", "", "", "拉布拉卡", "千里之功，只差一步了……")
simazhao:addSkill(choufa)
simazhao:addSkill(zhaoran)

return extension
